package com.macro.mall.seckill.job;

import cn.hutool.core.date.DateUtil;
import com.alibaba.fastjson.JSONObject;
import com.macro.mall.common.constant.RedisConstant;
import com.macro.mall.common.service.RedisService;
import com.macro.mall.mapper.SmsFlashPromotionMapper;
import com.macro.mall.model.PmsProduct;
import com.macro.mall.model.SmsFlashPromotion;
import com.macro.mall.model.SmsFlashPromotionExample;
import com.macro.mall.model.SmsFlashPromotionProductRelation;
import com.macro.mall.seckill.dao.SmsFlashPromotionDao;
import com.macro.mall.seckill.domain.PromotionProductInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @program: mall
 * @ClassName: SecKillWarmUpJob
 * @description: 秒杀活动缓存预热定时任务
 * @author: tan
 * @create: 2024-05-08 10:21
 **/
@Component
public class SecKillWarmUpJob {
    private Logger logger = LoggerFactory.getLogger(SecKillWarmUpJob.class);
    @Autowired
    private SmsFlashPromotionMapper smsFlashPromotionMapper;

    @Autowired
    private SmsFlashPromotionDao smsFlashPromotionDao;

    @Autowired
    private RedisService redisService;

    /**
     * cron表达式：Seconds Minutes Hours DayofMonth Month DayofWeek [Year]
     * 每10分钟扫描一次
     */
    @Scheduled(cron = "0 0/30 * * * ?")
    public void pushSpikeInfoIntoRedis(){
        //查询未开始的秒杀活动信息
        List<SmsFlashPromotion> promotionNotStartromotionNotStart = getSmsFlashPromotionNotStart();
        if(CollectionUtils.isEmpty(promotionNotStartromotionNotStart)){
            logger.info("无秒杀活动");
            return;
        }
        logger.info("秒杀活动列表:{}",JSONObject.toJSONString(promotionNotStartromotionNotStart));
        //查询秒杀的商品信息
        String format = DateUtil.format(LocalDateTime.now(), "yyyy-MM-dd");
        List<PromotionProductInfo> promotionNotStartProduct = getSmsFlashPromotionNotStartProduct(format);
        //根据活动id分组
        Map<Long, List<PromotionProductInfo>> groupPromotionNotStartProduct = promotionNotStartProduct.stream().collect(Collectors.groupingBy(SmsFlashPromotion::getId));
        //活动存入缓存
        for(SmsFlashPromotion smsFlashPromotion:promotionNotStartromotionNotStart){
            redisService.hSet(RedisConstant.REDIS_FLASH_PROMOTION,smsFlashPromotion.getId().toString(),smsFlashPromotion);
        }
        //活动产品信息存入缓存
        for(Map.Entry entry : groupPromotionNotStartProduct.entrySet()){
            Long promotionId = (Long) entry.getKey();
            List<PromotionProductInfo> smsFlashPromotions = (List<PromotionProductInfo>)entry.getValue();
            for(PromotionProductInfo promotionProductInfo : smsFlashPromotions){
                for (PmsProduct pmsProduct : promotionProductInfo.getProductList()){
                    redisService.hSet(RedisConstant.REDIS_FLASH_PROMOTION_PRODUCT+promotionId,pmsProduct.getId().toString(),pmsProduct);
                }
                for (SmsFlashPromotionProductRelation smsFlashPromotionProductRelation : promotionProductInfo.getSmsFlashPromotionProductRelationList()){
                    //秒杀限购数量
                    Integer flashPromotionCount = smsFlashPromotionProductRelation.getFlashPromotionCount();
                    List<Long> productIds = new ArrayList<>(smsFlashPromotionProductRelation.getFlashPromotionCount());
                    for (int i =0;i<flashPromotionCount;i++){
                        productIds.add(smsFlashPromotionProductRelation.getProductId());
                    }
                    Long size = redisService.lSize(RedisConstant.REDIS_FLASH_PROMOTION_COUNT + promotionId + "_" + smsFlashPromotionProductRelation.getProductId());
                    if(size<Long.valueOf(flashPromotionCount)){
                        //库存使用队列，单线程 原子性
                        redisService.lPushAll(RedisConstant.REDIS_FLASH_PROMOTION_COUNT+promotionId+"_"+smsFlashPromotionProductRelation.getProductId(),productIds);
                    }
                }
            }
        }
    }

    /**
     * cron表达式：Seconds Minutes Hours DayofMonth Month DayofWeek [Year]
     * 每10分钟扫描一次 移出过期的秒杀商品缓存
     */
    @Scheduled(cron = "0 0/30 * * * ?")
    public void removeSpikeInfoRedis(){
        List<Long> expireFlashPromotion = new ArrayList<>();
        //获取活动缓存
        List<SmsFlashPromotion> smsFlashPromotions = (List<SmsFlashPromotion>)redisService.hValues(RedisConstant.REDIS_FLASH_PROMOTION);
        for (SmsFlashPromotion smsFlashPromotion:smsFlashPromotions){
            LocalDateTime localDateTime = LocalDateTime.now();
            Date endDate = smsFlashPromotion.getEndDate();
            LocalDateTime endDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(endDate.getTime()), ZoneId.systemDefault());
            //保存已过期的秒杀活动id
            if(localDateTime.isAfter(endDateTime)){
                logger.info("移出过期秒杀活动:",smsFlashPromotion.getId());
                expireFlashPromotion.add(smsFlashPromotion.getId());
                redisService.hDel(RedisConstant.REDIS_FLASH_PROMOTION,smsFlashPromotion.getId().toString());
            }
        }
        //删除过期活动的商品信息
        for (Long id : expireFlashPromotion){
            logger.info("移出过期秒杀商品:",id);
            redisService.hDel(RedisConstant.REDIS_FLASH_PROMOTION_PRODUCT+id);
        }

    }


    /**
     * 查询未开始的秒杀活动
     * @author tan
     * @date 2024/6/5 11:00
     */
    private List<SmsFlashPromotion> getSmsFlashPromotionNotStart(){
        SmsFlashPromotionExample smsFlashPromotionExample = new SmsFlashPromotionExample();
        smsFlashPromotionExample.createCriteria()
                .andStartDateGreaterThan(new Date());
        List<SmsFlashPromotion> smsFlashPromotions = smsFlashPromotionMapper.selectByExample(smsFlashPromotionExample);
        return smsFlashPromotions;

    }

    private List<PromotionProductInfo> getSmsFlashPromotionNotStartProduct(String startDate){
        return smsFlashPromotionDao.getSmsFlashPromotionNotStartProduct(startDate);
    }
}

